package com.dwd.user.service.impl;

import com.dwd.basic.constant.PetLoginConstant;
import com.dwd.basic.constant.VerificationConstant;
import com.dwd.basic.exception.CustomException;
import com.dwd.basic.service.impl.BaseServiceImpl;
import com.dwd.basic.utils.AjaxResult;
import com.dwd.basic.utils.MD5Utils;
import com.dwd.basic.utils.StrUtils;
import com.dwd.check.pojo.Check;
import com.dwd.org.mapper.EmployeeMapper;
import com.dwd.org.pojo.Employee;
import com.dwd.org.service.IEmployeeService;
import com.dwd.org.service.impl.EmployeeServiceImpl;
import com.dwd.user.mapper.LoginInfoMapper;
import com.dwd.user.mapper.UserMapper;
import com.dwd.user.pojo.LoginInfo;
import com.dwd.user.pojo.User;
import com.dwd.user.pojo.dto.LoginInfoDto;
import com.dwd.user.service.ILoginInfoService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Description: TODO
 * @Author 汶狄
 * @Date 2021/1/18 11:35
 * @Version 1.0
 */
@Service
public class LoginInfoServiceImpl extends BaseServiceImpl<LoginInfo> implements ILoginInfoService {

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private IEmployeeService employeeService;

    /**
     * 登陆
     *
     * @param loginInfoDto
     */
    @Override
    public AjaxResult  login(LoginInfoDto loginInfoDto) throws CustomException {
        //校验数据
        checkLoginData(loginInfoDto);
        //根据用户名 email 电话 和type查询是否存在数据
        LoginInfo loginInfo = loginInfoMapper.queryByUsernameAndType(loginInfoDto.getUsername(), loginInfoDto.getType());
        //判断是否存在
        if (loginInfo == null) {
            throw new CustomException("用户名不存在");
        }
        //如果免密登陆之后，没有密码，直接返回密码错误
        if (StringUtils.isEmpty(loginInfo.getPassword())) {
            throw new CustomException("密码错误");
        }
        //判断密码，把前端数据，进行加密再次比较
        if (!loginInfo.getPassword().equals(MD5Utils.encrypByMd5(loginInfoDto.getPassword() + loginInfo.getSalt()))) {
            throw new CustomException("密码错误");
        }

       /* //获取当前用户对象
        Subject subject = SecurityUtils.getSubject();
        //封装token 令牌
        UsernamePasswordToken token1 = new UsernamePasswordToken(loginInfo.getUsername(), loginInfo.getPassword());
        subject.login(token1);*/

        //随机产生token
        String token = UUID.randomUUID().toString();
        //把查询的数据存入redis  key为token value 为 对象
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        //扩展登陆查询用户
        Employee loginUser = userMapper.queryEmpByUsername(loginInfoDto.getUsername());
        //根据登陆用户的id查询员工id
        Long id = employeeService.queryempIdByLoginInfoId(loginInfo.getId());

        //查询登陆用户权限
        List<Long> ids = employeeMapper.queryPermisId(id);

        Map<String, Object> map = new HashMap<>(16);
        map.put("token", token);
        map.put("user", loginInfo);
        map.put("loginUser", loginUser);
        //返回token和用户信息
        return AjaxResult.me().setResultObj(map);
    }

    /**
     * 校验登陆数据
     *
     * @param loginInfoDto
     * @throws CustomException
     */
    private void checkLoginData(LoginInfoDto loginInfoDto) throws CustomException {
        //账号不能为空
        if (StringUtils.isEmpty(loginInfoDto.getUsername())) {
            throw new CustomException("用户不能为空");
        }
        //密码不能为空
        if (StringUtils.isEmpty(loginInfoDto.getPassword())) {
            throw new CustomException("密码不能为空");
        }
        //type不能为空
        if (loginInfoDto.getType() == null) {
            throw new CustomException("补全登陆信息");
        }
    }

    /**
     * 验证码登陆  免密登陆
     *
     * @param loginInfoDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> loginVer(LoginInfoDto loginInfoDto) throws CustomException {
        Map<String, Object> map = new HashMap<>(16);
        //校验数据
        checkLoginVerData(loginInfoDto);
        //根据手机号查询loginfo里面是否存在，存在，说明已经注册过了，直接设置30分钟，返回token user 跳转主页
        LoginInfo loginInfo = loginInfoMapper.queryByUsernameAndType(loginInfoDto.getPhone(), loginInfoDto.getType());
        if (loginInfo == null) {
            //不存在 说明第一次 存入logininfo表 再存入user表 设置30分钟 返回token user 跳转主页
            //创建loginInfo对象
            loginInfo = createLoginInfo(loginInfoDto);
            //存入logininfo
            loginInfoMapper.save(loginInfo);
            //创建user
            User user = createUser(loginInfo);
            //存入user
            userMapper.save(user);
        }
        String token = UUID.randomUUID().toString();
        //设置30分钟
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        //返回token user
        map.put("token", token);
        map.put("user", loginInfo);
        return map;

    }


    /**
     * 校验验证码登陆数据
     *
     * @param loginInfoDto
     * @throws CustomException
     */
    private void checkLoginVerData(LoginInfoDto loginInfoDto) throws CustomException {
        //账号不能为空
        if (StringUtils.isEmpty(loginInfoDto.getPhone())) {
            throw new CustomException("手机号不能为空");
        }
        //手机号码是否正确
        String Regex = "(?:(?:(?:13[0-9])|(?:14[57])|(?:15[0-35-9])|(?:19[0-35-9])|(?:17[36-8])|(?:18[0-9]))\\d{8})|(?:170[057-9]\\d{7})";
        if (!loginInfoDto.getPhone().matches(Regex)) {
            throw new CustomException("请输入正确的手机号码");
        }
        if (StringUtils.isEmpty(loginInfoDto.getCode())) {
            throw new CustomException("验证码不能为空");
        }
        //获取验证码 GBCG:1611064055506
        String value = (String) redisTemplate.opsForValue().get(loginInfoDto.getPhone() + ":" + VerificationConstant.WECHAT_BINDER);
        //验证码是否过期
        if (StringUtils.isEmpty(value)) {
            throw new CustomException("验证码过期");

        }
        //验证码是否正确
        if (!loginInfoDto.getCode().toLowerCase().equals(value.split(":")[0].toLowerCase())) {
            throw new CustomException("验证码错误");
        }
        //type不能为空
        if (loginInfoDto.getType() == null) {
            throw new CustomException("补全登陆信息");
        }
    }


    /**
     * 创建user，用来存到user表
     *
     * @param loginInfo
     * @return
     */
    private User createUser(LoginInfo loginInfo) {
        User user = new User();
        BeanUtils.copyProperties(loginInfo, user);
        //设置状态
        user.setState(PetLoginConstant.HOME);
        //设置logininfo
        user.setLoginInfo(loginInfo);
        return user;
    }

    /**
     * 创建LoginInfo，用来存到LoginInfo表,没有密码，没有盐值
     *
     * @param loginInfoDto
     * @return
     */
    private LoginInfo createLoginInfo(LoginInfoDto loginInfoDto) {
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setType(loginInfoDto.getType()).setPhone(loginInfoDto.getPhone());
        return loginInfo;
    }

    /**
     * 忘记密码手机之验证码 数据校验  找回
     *
     * @param loginInfoDto
     * @return
     * @throws CustomException
     */
    @Override
    public AjaxResult forget(LoginInfoDto loginInfoDto) throws CustomException {
        //账号不能为空
        if (StringUtils.isEmpty(loginInfoDto.getPhone())) {
            throw new CustomException("手机号不能为空");
        }
        //手机号码是否正确
        String Regex = "(?:(?:(?:13[0-9])|(?:14[57])|(?:15[0-35-9])|(?:19[0-35-9])|(?:17[36-8])|(?:18[0-9]))\\d{8})|(?:170[057-9]\\d{7})";
        if (!loginInfoDto.getPhone().matches(Regex)) {
            throw new CustomException("请输入正确的手机号码");
        }
        //根据手机号查询loginfo里面是否存在，不存在，说明没有注册
        LoginInfo loginInfo = loginInfoMapper.queryByUsernameAndType(loginInfoDto.getPhone(), loginInfoDto.getType());
        if (loginInfo == null) {
            throw new CustomException("此手机号码尚未注册，请检查填写是否正确");
        }
        if (StringUtils.isEmpty(loginInfoDto.getCode())) {
            throw new CustomException("验证码不能为空");
        }
        //获取验证码 GBCG:1611064055506
        String value = (String) redisTemplate.opsForValue().get(loginInfoDto.getPhone() + ":" + VerificationConstant.WECHAT_BINDER);
        //验证码是否过期
        if (StringUtils.isEmpty(value)) {
            throw new CustomException("验证码过期");

        }
        //验证码是否正确
        if (!loginInfoDto.getCode().toLowerCase().equals(value.split(":")[0].toLowerCase())) {
            throw new CustomException("验证码错误");
        }
        //type不能为空
        if (loginInfoDto.getType() == null) {
            throw new CustomException("补全登陆信息");
        }

        return AjaxResult.me().setResultObj(loginInfo.getPhone());
    }


    /**
     * 忘记密码之存储密码
     *
     * @param loginInfoDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void found(LoginInfoDto loginInfoDto) throws CustomException {
        //校验数据 密码 重复密码是否相等 为空之类
        checkPassword(loginInfoDto);
        checkconfirmpassword(loginInfoDto);
        //创建logininfo
        LoginInfo loginInfo = createLoginInfoAndPass(loginInfoDto);
        //把密码存入logininfo表 盐值  根据手机号修改
        loginInfoMapper.updateLoginInfoByPhone(loginInfo);
        //把密码存入user表  根据手机号修改
        User user = createUserAndPass(loginInfo);
        loginInfoMapper.updateUserByPhone(user);
    }

    /**
     * 设置盐值和密码
     *
     * @param loginInfo
     * @return
     */
    private User createUserAndPass(LoginInfo loginInfo) {
        User user = new User();
        //把密码和盐值拷贝
        BeanUtils.copyProperties(loginInfo, user);
        return user;
    }

    /**
     * 创建LoginInfo，用来存到LoginInfo表,有密码，有盐值
     *
     * @param loginInfoDto
     */
    private LoginInfo createLoginInfoAndPass(LoginInfoDto loginInfoDto) {
        LoginInfo loginInfo = new LoginInfo();
        //设置手机
        loginInfo.setPhone(loginInfoDto.getPhone());
        //设置盐值 随机产生10位
        loginInfo.setSalt(StrUtils.getComplexRandomString(10));
        //设置密码 带上盐值
        loginInfo.setPassword(MD5Utils.encrypByMd5(loginInfoDto.getPassword() + loginInfo.getSalt()));
        return loginInfo;
    }

    /**
     * 校验密码
     *
     * @param loginInfoDto
     * @throws CustomException
     */
    @Override
    public void checkPassword(LoginInfoDto loginInfoDto) throws CustomException {
        checkPassword1(loginInfoDto);
    }

    @Override
    public void checkConfirmpassword(LoginInfoDto loginInfoDto) throws CustomException {
        checkconfirmpassword(loginInfoDto);
    }

    /**
     * 邮箱校验
     *
     * @param loginInfoDto
     * @return
     * @throws CustomException
     */
    @Override
    public AjaxResult forgetEmail(LoginInfoDto loginInfoDto) throws CustomException {
        //邮箱是否为空
        if (StringUtils.isEmpty(loginInfoDto.getEmail())) {
            throw new CustomException("邮箱不能为空");
        }
        //邮箱是否合法
        String regex = "^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$";
        String email = loginInfoDto.getEmail();
        if (!email.matches(regex)) {
            throw new CustomException("邮箱不合法");
        }
        //邮箱是否存在
        LoginInfo loginInfo = loginInfoMapper.queryByUsernameAndType(loginInfoDto.getEmail(), loginInfoDto.getType());
        if (loginInfo == null) {
            throw new CustomException("没有该邮箱");
        }

        if (StringUtils.isEmpty(loginInfoDto.getCode())) {
            throw new CustomException("验证码不能为空");
        }
        //获取邮箱验证码 GBCG:1611064055506
        String value = (String) redisTemplate.opsForValue().get(loginInfoDto.getEmail() + ":" + VerificationConstant.USER_EMAIL);
        //验证码是否过期
        if (StringUtils.isEmpty(value)) {
            throw new CustomException("验证码过期");

        }
        //验证码是否正确
        if (!loginInfoDto.getCode().toLowerCase().equals(value.split(":")[0].toLowerCase())) {
            throw new CustomException("验证码错误");
        }
        //type不能为空
        if (loginInfoDto.getType() == null) {
            throw new CustomException("补全登陆信息");
        }

        return AjaxResult.me().setResultObj(loginInfo.getEmail());
    }


    /**
     * 校验密码 长度之类
     *
     * @param loginInfoDto
     * @throws CustomException
     */
    private void checkPassword1(LoginInfoDto loginInfoDto) throws CustomException {
        if (StringUtils.isEmpty(loginInfoDto.getPassword())) {
            throw new CustomException("密码不能为空");
        }
        //密码长度为6到20位,必须包含字母和数字，字母区分大小写
        String regex = "^(?=.*[0-9])(?=.*[a-zA-Z])(.{6,20})$";
        if (!loginInfoDto.getPassword().matches(regex)) {
            throw new CustomException("密码长度为6到20位,必须包含字母和数字，字母区分大小写");
        }
    }

    /**
     * 校验重复密码
     *
     * @param loginInfoDto
     * @throws CustomException
     */
    private void checkconfirmpassword(LoginInfoDto loginInfoDto) throws CustomException {
        if (StringUtils.isEmpty(loginInfoDto.getConfirmpassword())) {
            throw new CustomException("重复密码不能为空");
        }
        if (!loginInfoDto.getPassword().equals(loginInfoDto.getConfirmpassword())) {
            throw new CustomException("两次密码不一致");
        }
    }

}
